﻿using BingSpatialDataServiceExcelAddin.BSDSTools.Models;
using System;
using System.IO;
using System.Text.RegularExpressions;

namespace BingSpatialDataServiceExcelAddin.BSDSTools.FileTools
{
    public class DataSourceReader : DelimitedReader, IDisposable
    {
        #region Private Properties

        private string _entityType;
        private ColumnHeader _header;

        #endregion

        #region Constructor

        ///<summary>
        /// Constructor
        ///</summary>
        public DataSourceReader(Stream stream, char delimiter)
            : base(stream, delimiter)
        {
            ReadFileHeader();
        }

        public DataSourceReader(StreamReader stream, char delimiter)
            : base(stream, delimiter)
        {
            ReadFileHeader();
        }

        #endregion

        #region Public Properties

        public ColumnHeader Header
        {
            get
            {
                return _header;
            }
        }

        public string EntityType
        {
            get
            {
                return _entityType;
            }
        }

        #endregion

        #region Public Methods

        void IDisposable.Dispose()
        {
            base.Dispose();
            _header = null;
            _entityType = null;
        }

        #endregion

        #region Private Methods

        private void ReadFileHeader()
        {
            string[] row = this.GetNextRow();

            while (row != null && row.Length == 1)
            {
                row = this.GetNextRow();
            }

            if (row != null && row.Length > 0)
            {
                //File defines the Version String
                if (row[0].StartsWith("Bing Spatial Data Services", StringComparison.OrdinalIgnoreCase))
                {
                    if (row.Length >= 4)
                    {
                        _entityType = row[3];
                    }

                    //Read next row to get header
                    ReadFileHeader();
                }
                //MapPoint data sources may contain an encoding string
                else if (row[0].StartsWith("<encoding name='UTF-8'>", StringComparison.OrdinalIgnoreCase))
                {
                    //Read next row to get header
                    ReadFileHeader();
                }
                else
                {
                    //Parse Column Header
                    _header = ParseColumnHeader(row);

                    //Add primary Key if not already added
                    if (!_header.HasPrimaryKey())
                    {
                        int idx = _header.GetIndex("EntityID");

                        if (idx >= 0)
                        {
                            _header[idx].IsPrimaryKey = true;
                        }
                    }
                }
            }
        }

        private ColumnHeader ParseColumnHeader(string[] row)
        {
            ColumnHeader ch = new ColumnHeader();

            Regex ColumnHeaderRegex = new Regex(Constants.ColumnHeaderPattern);

            string name = string.Empty, typeName = string.Empty;
            bool isPrimaryKey = false;
            int len = row.Length;

            for (int i = 0; i < row.Length; i++)
            {
                isPrimaryKey = false;
                name = row[i];
                typeName = DataTypes.String;

                if (i < len - 1 && row[i + 1].StartsWith("primaryKey)", StringComparison.OrdinalIgnoreCase))
                {
                    name = name.Substring(0, name.IndexOf('('));

                    //Parse out type name
                    typeName = row[i].Substring(row[i].IndexOf('('));

                    if (typeName.Contains(","))
                    {
                        typeName = typeName.Replace(",", "");
                    }

                    isPrimaryKey = true;
                    i++;
                }
                else if (ColumnHeaderRegex.IsMatch(name))
                {
                    Match m = ColumnHeaderRegex.Match(name);

                    //Expect column header to look like:
                    //OData: EntityID(Edm.String,primarykey) or Longitude(Edm.Double)
                    //MapPoint: EntityID(long) or AddressLine(String)
                    //Multimap: client_id()
                    if (m.Success && m.Groups.Count >= 3)
                    {
                        name = m.Groups[1].Value;
                        typeName = m.Groups[2].Value;

                        if (typeName.IndexOf(",") > -1)
                        {
                            typeName = typeName.Substring(0, typeName.IndexOf(","));
                        }

                        if (m.Groups[2].Value.IndexOf("primaryKey", StringComparison.OrdinalIgnoreCase) > -1)
                        {
                            isPrimaryKey = true;
                        }
                    }
                }

                //Validate Data Type
                switch (typeName.ToLowerInvariant())
                {
                    case "edm.int64":
                    case "int":
                    case "long":
                        typeName = DataTypes.Long;
                        break;
                    case "edm.double":
                    case "float":
                    case "double":
                        typeName = DataTypes.Double;
                        break;
                    case "edm.boolean":
                    case "bool":
                    case "boolean":
                        typeName = DataTypes.Boolean;
                        break;
                    case "edm.datetime":
                    case "date":
                    case "datetime":
                        typeName = DataTypes.DateTime;
                        break;
                    case "edm.string":
                    case "varchar":     //Multimap
                    case "text":        //Multimap
                    default:
                        typeName = DataTypes.String;
                        break;
                }

                ch.Add(new ColumnDefinition()
                {
                    Name = name,
                    TypeName = typeName,
                    IsPrimaryKey = isPrimaryKey
                });
            }

            return ch;
        }

        #endregion
    }
}
